<?php

/**
 * Kit.
 *
 * Form library.
 *
 * @version $Id: form.php 315 2010-04-23 06:06:53Z perfilev $
 * @package System
 */

abstract class Form_Field
{
	/**
     * @var object  Owner.
     */
    public $form;

	/**
     * @var string  Name.
     */
    public $name;

	/**
	 * @var string  Label.
	 */
	public $label;

    /**
     * @var string  Value.
     */
    public $value;

	/**
	 * @var array  Attributes.
	 */
	public $attributes = array();

    /**
     * @var boolean  Whether to group field with the previous one?
     */
    public $bunched = false;

    /**
     * @var boolean  Whether this field is a phantom?
     */
    public $phantom = false;

    /**
     * @var boolean  Whether to interpret empty value as null?
     */
    public $nullable = false;

    /**
     * @var array  Validation errors.
     */
    public $errors;

	/**
	 * @var boolean  Whether this field is required?
	 */
	public $required = false;

	/**
	 * @var array  Validation rules.
	 */
	public $rules;

	/**
	 * @var array  Validation callbacks.
	 */
	public $callbacks;

	/**
	 * @var boolean  Whether field is modified?
	 */
	public $modified = false;

	/**
	 * @var string  Fieldset of the element
	 */
	public $fieldset = false;

	/**
	 * @var string  Is element already rendered?
	 */
	public $rendered = false;

	/**
	 * @var array  Error messages.
	 */
	public $ruleTexts = array(
		'required'  => 'This field is required',
		'minLength' => 'The minimum length for this field is {0}',
		'maxLength' => 'The maximum length for this field is {0}',
		'regex'     => 'Value does not match pattern {0}',
 		'alias'     => 'Only letters, numbers, "-" and "." are allowed',
		'alpha'     => 'Only letters and "_" are allowed',
		'alphanum'  => 'Only letters, numbers and "_" are allowed',
		'date'      => 'Date must be in the format "YYYY-MM-DD"',
		'datetime'  => 'Date and Time must be in format "YYYY-MM-DD HH:MM:SS"',
		'minValue'  => 'The minimum value for this field is {0}',
		'maxValue'  => 'The maximum value for this field is {0}',
		'integer'   => 'Value is not a valid integer',
		'float'     => 'Value is not a valid decimal number',
		'email'     => 'Value is not a valid e-mail address',
		'url'       => 'Value is not a valid URL address',
		'ip'        => 'Value is not a valid IP address',
		'fileExt'   => 'Allowed file extensions: {0}',
	);

	/**
	 * Constructor.
	 *
	 * @param object $form   Owner.
	 * @param string $name   Name.
	 * @param string $label  Label.
	 * @param string $value  Value.
	 * @return this
	 * @throws InvalidArgumentException
	 */
	public function __construct(Form $form, $name, $label, $value)
	{
		$this->form  = $form;
		$this->name  = $name;
		$this->label = $label;
		$this->value = $value;
		$this->fieldset = $form->fieldset;
		$form->fieldsets[$form->fieldset]['elements'][] = $name;
	}

	/**
	 * Helper for render();
	 */
	public function composeName()
	{
		if (substr($this->name, -2) == '[]') {
			$name = substr($this->name, 0, -2);
			return $this->form->attributes['id'] . '[' . $name . '][]';
		} else {
			return $this->form->attributes['id'] . '[' . $this->name . ']';
		}
	}

	/**
	 * Helper for render();
	 */
	public function composeId()
	{
		if (substr($this->name, -2) == '[]') {
			$name = substr($this->name, 0, -2);
			return $this->form->attributes['id'] . '-' . $name;
		} else {
			return $this->form->attributes['id'] . '-' . $this->name;
		}
	}

	/**
	 * Sets $required to true.
	 *
	 * @param boolean $value  Is the filed required?
	 * @return this
	 */
	public function required($value = true)
	{
	    $this->required = $value;
	    return $this;
	}

	/**
	 * Sets $bunched value.
	 *
	 * @param boolean $value  Is this the bunched field?
	 * @return this
	 */
	public function bunched($value = true)
	{
	    $this->bunched = $value;
	    return $this;
	}

	/**
	 * Sets $phantom value.
	 *
	 * @param boolean $value  Is this the phantom field?
	 * @return this
	 */
	public function phantom($value = true)
	{
	    $this->phantom = $value;
	    return $this;
	}

	/**
	 * Sets $nullable value.
	 *
	 * @param boolean $value  Returns null for empty value?
	 * @return this
	 */
	public function nullable($value = true)
	{
	    $this->nullable = $value;
	    return $this;
	}

	/**
	 * Adds callback to the element.
	 *
	 * @param function $handler  Callback handler.
	 * @return this
	 */
	public function callback($handler, $args = array())
	{
		$this->callbacks[] = array(
			'handler' => $handler,
			'args'    => $args
		);
		return $this;
	}

	/**
	 * Adds element attribute.
	 *
	 * @param string $name   Attribute name.
	 * @param string $value  Attribute value.
	 * @return this
	 */
	public function attribute($name, $value = null)
	{
		$this->attributes[$name] = (null === $value) ? $name : $value;
		return $this;
	}

	/**
	 * Adds validation rule to element.
	 *
	 * @param string $type   Rule type.
	 * @param mixed  $value  Rule value.
	 * @param string $text   Rule text.
	 * @return this
	 */
	public function rule($type, $value = null, $text = null)
	{
		if ('required' == $type) {
			$this->required = true;
		} elseif (array_key_exists($type, $this->ruleTexts)) {
			if ('maxLength' == $type) {
				$this->attribute('maxLength', $value);
			}
			if (null !== $text) {
				$this->ruleTexts[$type] = $text;
			}
			$this->rules[] = array($type, $value);
		} else {
			throw new InvalidArgumentException('Unsupported rule "' . $type . '".');
		}
		return $this;
	}

	/**
	 * Adds custom error while validating.
	 *
	 * @param string $message  Error message.
	 * @return this
	 */
	public function error($message)
	{
		$this->errors[] = $message;
		return $this;
	}

	/**
	 * Validate value according to set rules.
	 *
	 * @return boolean
	 */
	public function validate()
	{
		$this->errors = array();
		if ('' == $this->value && $this->required) {
			$this->errors[] = $this->ruleTexts['required'];
		}
		if ('' !== $this->value && $this->rules) {
			$number = false;
			foreach ($this->rules as $rule) {
				list($name, $value) = $rule;
				switch ($name) {
					case 'minLength':
						if (mb_strlen($this->value) < $value) {
							$this->errors[] = Text::format($this->ruleTexts[$name], $value);
						}
						break;
					case 'maxLength':
						if (mb_strlen($this->value) > $value) {
							$this->errors[] = Text::format($this->ruleTexts[$name], $value);
						}
						break;
					case 'regex':
						if (false == (bool) preg_match($value, $this->value)) {
							$this->errors[] = Text::format($this->ruleTexts[$name], $value);
						}
						break;
					case 'alias':
						if (false == (bool) preg_match('/^[a-z0-9.-]+$/us', $this->value)) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'date':
						if (false == (bool) preg_match('/^\d{4}-\d{2}-\d{2}$/us', $this->value)) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'datetime':
						if (false == (bool) preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/us', $this->value)) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'alpha':
						if (false == (bool) preg_match('/^[a-z_]+$/usi', $this->value)) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'alphanum':
						if (false == (bool) preg_match('/^[a-z0-9_]+$/usi', $this->value)) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'integer':
						if (0 == preg_match('/^\d+$/', $this->value, $matches)) {
						//if (false === ($number = filter_var($this->value, FILTER_VALIDATE_INT))) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'float':
						if (false === ($number = filter_var($this->value, FILTER_VALIDATE_FLOAT))) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'minValue':
						if (false !== $number && $number < $value) {
							$this->errors[] = Text::format($this->ruleTexts[$name], $value);
						}
						break;
					case 'maxValue':
						if (false !== $number && $number > $value) {
							$this->errors[] = Text::format($this->ruleTexts[$name], $value);
						}
						break;
					case 'ip':
						if (false === filter_var($this->value, FILTER_VALIDATE_IP)) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'email':
						if (false === filter_var($this->value, FILTER_VALIDATE_EMAIL)) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'url':
						/*if (false === parse_url($this->value, PHP_URL_SCHEME)) {
							$this->value = 'http://' . $this->value;
						}*/
						if (false === filter_var($this->value, FILTER_VALIDATE_URL)) {
							$this->errors[] = $this->ruleTexts[$name];
						}
						break;
					case 'fileExt':
						if (false === in_array(strtolower(File::extension($this->value)), (array) $value)) {
							$this->errors[] = Text::format($this->ruleTexts[$name], implode(', ', (array) $value));
						}
						break;
				}
			}
		}
		if (0 == count($this->errors) && $this->callbacks) {
			foreach ($this->callbacks as $callback) {
				call_user_func($callback['handler'], $this, $callback['args']);
			}
		}
		return 0 == count($this->errors);
	}

	/**
	 * Sets a new value of the field (marks as modified).
	 *
	 * @param  string  $value  2New value of the field.
	 * @return this
	 */
	public function setValue($value)
	{
		if ($this->value != $value) {
			$this->value = $value;
			$this->modified = true;
		}
		return $this;
	}

	/**
	 * Retursn the value.
	 *
	 * @return mixed
	 */
	public function getValue()
	{
		return ('' == $this->value && $this->nullable) ? null : $this->value;
	}

	/**
     * Renders HTML.
     *
     * @return string
     */
    abstract public function render();
}

class Form_Field_Hidden extends Form_Field
{
	/**
	 * Constructor.
	 *
	 * @param object $form   Owner.
	 * @param string $name   Name.
	 * @param string $value  Value.
	 * @return this
	 * @throws InvalidArgumentException
	 */
	public function __construct(Form $form, $name, $value)
	{
		parent::__construct($form, $name, null, $value);
	}

	public function render()
	{
		return '<input type="hidden" name="' . $this->composeName()
		     . '" value="' . $this->value . '" />';
	}
}

class Form_Field_Text extends Form_Field
{
	public function render()
	{
		return '<input' . Html::attributes(array_merge($this->attributes, array(
			'type'  => 'text',
			'id'    => $this->composeId(),
			'name'  => $this->composeName(),
			'value' => $this->value
		))) . ' />';
	}
}

class Form_Field_Html extends Form_Field
{
	public function render()
	{
		return '<span' . Html::attributes(array_merge($this->attributes, array(
			'id'    => $this->composeId()
		))) . '>' . $this->value . '</span>';
	}
}

class Form_Field_Comment extends Form_Field
{

	/**
	 * Constructor.
	 *
	 * @param object $form   Owner.
	 * @param string $name   Name.
	 * @return this
	 * @throws InvalidArgumentException
	 */
	public function __construct(Form $form, $name)
	{
		parent::__construct($form, $name, null, null);
		$this->phantom = true;
	}

	public function render()
	{
		return '<!--' . $this->name . '!-->';
	}
}

class Form_Field_Password extends Form_Field
{
	public function render()
	{
		return '<input' . Html::attributes(array_merge($this->attributes, array(
			'type'  => 'password',
			'id'    => $this->composeId(),
			'name'  => $this->composeName(),
			'value' => $this->value
		))) . ' />';
	}
}

class Form_Field_Select extends Form_Field
{
	/**
	 * @var array  Element options.
	 */
	protected $options;

	/**
	 * Constructor.
	 *
	 * @param Form   $form   Owner.
	 * @param string $name   Name.
	 * @param array  $name   Options.
	 * @param string $label  Label.
	 * @param string $value  Value.
	 * @return this
	 */
	public function __construct(Form $form, $name, array $options, $label = null, $value = null)
	{
		parent::__construct($form, $name, $label, $value);
		$this->options = $options;
	}

	public function render()
	{
		$result = '<select' . Html::attributes(array_merge($this->attributes, array(
			'id'    => $this->composeId(),
			'name'  => $this->composeName(),
		))) . ">\n";

		foreach ($this->options as $key => $value) {
			$result .= chr(9) . '<option value="' . Html::chars($key) . '"'
			         . ($this->value == $key ? ' selected="selected"' : '')
			         . '>' . $value . "</option>\n";
		}
		return $result . '</select>';
	}
}

class Form_Field_Multiselect extends Form_Field
{
    /**
     * @var array  Element options.
     */
    protected $options;

    /**
     * Constructor.
     *
     * @param Form   $form   Owner.
     * @param string $name   Name.
     * @param array  $name   Options.
     * @param string $label  Label.
     * @param string $value  Value.
     * @return this
     */
    public function __construct(Form $form, $name, array $options, $label = null, $value = null)
    {
        parent::__construct($form, $name, $label, $value);
        $this->options = $options;

    }

    public function render() {
        $result = '<select multiple="true" style="height:200px;"' . Html::attributes(array_merge($this->attributes, array(
            'id' => $this->composeId(),
            'name' => $this->composeName() . '[]',
            //'name' => 'select1[]',
        ))) . ">\n";
        if (!is_array($this->value)) {
            $this->value = explode(',', $this->value);
        }
//        system::dump($this->value);exit();
            foreach ($this->options as $key => $value) {
            $result .= chr(9) . '<option value="' . $value. '"'
                . (in_array($value, $this->value) ? ' selected="selected"' : '')
                . '>' . $value. "</option>\n";
        }
//        foreach($this->value as $key=>$selected){
//              $result .= chr(9) . '<option value="' . $selected. '"'
//                . ' selected="selected"'
//                . '>' . $selected. "</option>\n";
////            unset($this->options[$selected]);
//        }
        return $result . '</select>';
    }
}
class Form_Field_Radio extends Form_Field
{
	/**
	 * @var array  Options.
	 */
	protected $options;

	/**
	 * Constructor.
	 *
	 * @param Form   $form   Owner.
	 * @param string $name   Name.
	 * @param array  $name   Options.
	 * @param string $label  Label.
	 * @param string $value  Value.
	 * @return this
	 */
	public function __construct(Form $form, $name, $options, $label = null, $value = null)
	{
	    parent::__construct($form, $name, $label, $value);
		$this->options = $options;
	}

	public function render()
	{
		$id = $this->composeId() . '-';
		$result = '';
		foreach ($this->options as $key => $value) {
			$result .= '<input' . Html::attributes(array_merge($this->attributes, array(
				'type'  => 'radio',
				'id'    => $id . $key,
				'name'  => $this->composeName(),
				'value' => $key,
			))) . ($this->value == $key ? ' checked="checked"' : '') . ' />&nbsp;<label for="' . $id . $key . '">' . $value . '</label>&nbsp;';
		}
		return $result;
	}
}

class Form_Field_Checkbox extends Form_Field
{
	/**
	 * @var string  Checkbox label.
	 */
	protected $boxLable;

	/**
	 * Constructor.
	 *
	 * @param Form   $form      Owner.
	 * @param string $name      Name.
	 * @param string $label     Label.
	 * @param string $boxLabel  Checkbox label.
	 * @param string $value     Value.
	 * @return this
	 */
	public function __construct(Form $form, $name, $label = null, $boxLabel = null, $value = null)
	{
		parent::__construct($form, $name, $label, $value);
		$this->boxLabel = $boxLabel;
	}

	public function render()
	{
		$id   = $this->composeId();
		$html = '<input' . Html::attributes(array_merge($this->attributes, array(
			'type'  => 'checkbox',
			'id'    => $id,
			'name'  => $this->composeName(),
		))) . ($this->value ? ' checked' : '') . ' value="1" />';
		if ($this->boxLabel) {
			$html .= '&nbsp;<label for="' . $id . '">' . $this->boxLabel . '</label>';
		}
		return $html;
	}
}

class Form_Field_Textarea extends Form_Field
{
	public function render()
	{
		return '<textarea' . Html::attributes(array_merge($this->attributes, array(
			'type'  => 'checkbox',
			'id'    => $this->composeId(),
			'name'  => $this->composeName(),
		))) . '>' . Html::chars($this->value) . '</textarea>';
	}
}

class Form_Field_Submit extends Form_Field
{
	/**
	 * @var array  Alternative link (eg. [Submit] or `Cancel`) - array of arguments to Html::anchor().
	 */
	public $altLink;

	/**
	 * Constructor.
	 *
	 * @param Form   $form     Owner.
	 * @param string $name     Name.
	 * @param string $value    Value.
	 * @param array  $altLink  Alternative Link.
	 * @return this
	 */
	public function __construct(Form $form, $name, $value = null, $altLink = null)
	{
	    parent::__construct($form, $name, null, $value);
		$this->altLink = $altLink;
	}

	public function render()
	{
		$html = '<input' . Html::attributes(array_merge($this->attributes, array(
			'type'  => 'submit',
			'id'    => $this->composeId(),
			'name'  => $this->composeName(),
			'value' => $this->value
		))) . ' />';
		if ($this->altLink) {
			$html .= '&nbsp;or&nbsp;' . call_user_func_array(array('html', 'anchor'), $this->altLink);
		}
		return $html;
	}
}

class Form_Field_File extends Form_Field
{
	/**
	 * @var array  Uploaded files.
	 */
	public $files = null;

	/**
	 * @var array  Upload error messages.
	 */
	public $warnings = array();

	/**
	 * @var array  Error messages.
	 */
	public $ruleTexts = array(
		'required'  => 'This field is required',
		'mime'      => 'File "{0}" is of {1} type. Only {2} allowed.',
		'extension' => 'File "{0}" has restricted extension. Only {2} allowed.'
	);

	/**
	 * Constructor.
	 *
	 * @param Form   $form   Owner.
	 * @param string $name   Name.
	 * @param string $label  Label.
	 * @return this
	 */
	public function __construct(Form $form, $name, $label = null)
	{
		parent::__construct($form, $name, $label, null);
		$this->form->attributes['enctype'] = 'multipart/form-data';
	}

	/**
	 * Returns upload error message by its number.
	 *
	 * @param integer $errorNo  Error number.
	 * @return string
	 */
	public static function getUploadErrorMessage($errorNo)
	{
		switch ($errorNo) {
			case UPLOAD_ERR_INI_SIZE:
				return 'File "{0}" exceeds the upload_max_filesize directive.';
			case UPLOAD_ERR_FORM_SIZE:
				return 'File "{0}" exceeds the MAX_FILE_SIZE directive.';
			case UPLOAD_ERR_PARTIAL:
				return 'File "{0}" was only partially uploaded.';
			case UPLOAD_ERR_NO_FILE:
				return 'No file was uploaded.';
			case UPLOAD_ERR_NO_TMP_DIR:
				return 'Missing a temporary folder.';
			case UPLOAD_ERR_CANT_WRITE:
				return 'Failed to write file "{0}" to disk.';
			case UPLOAD_ERR_EXTENSION:
				return 'File upload stopped by extension.';
			default:
				return 'Don\'t really know what\'s up with "{0}".';
		}
	}

	/**
	 * Validate value according to set rules.
	 *
	 * @return boolean
	 */
	public function validate()
	{
		$this->errors = array();
		if (array_key_exists($this->name, $_FILES)) {
			$files = array();

			// Collect uploaded files without errors
			for ($i = 0, $c = count($_FILES[$this->name]['error']); $i < $c; $i++) {
				if ($name  = $_FILES[$this->name]['name'][$i]) {
					$error = $_FILES[$this->name]['error'][$i];
					if (0 == $error) {
						$files[] = array(
							'key'       => $i,
							'name'      => $name,
							'type'      => $_FILES[$this->name]['type'][$i],//File::mime($_FILES[$this->name]['tmp_name'][$i]),
							'extension' => strtolower(File::extension($_FILES[$this->name]['name'][$i])),
							'tmp_name'  => $_FILES[$this->name]['tmp_name'][$i],
							'size'      => $_FILES[$this->name]['size'][$i],
						);
					} else {
						$this->warnings[] = Text::format(self::getUploadErrorMessage($error), $name);
					}
				}
			}

			// Filter files by rules
			if ($this->rules) {
				foreach ($this->rules as $rule) {
					list($name, $value) = $rule;
					if ('mime' == $name || 'extension' == $name) {
						$value = (array) $value;
						foreach ($files as $i => $file) {
							switch ($name) {
								case 'extension':
									$validationArray = $file['extension'];
									break;
								case 'mime':
									$validationArray = $file['type'];
									break;
							}
							if (false === in_array($validationArray, $value)) {
								$this->warnings[] = Text::format(
									$this->ruleTexts[$name],
									$file['name'],
									$file['type'],
									implode(', ', $value)
								);
								unset($files[$i]);
							}
						}
					}
				}
			}

			if (count($files) > 0) {
				$this->files = array_values($files);
			}
		}

		if (null === $this->files && $this->required) {
			$this->errors[] = $this->ruleTexts['required'];
		}

		if (0 == count($this->errors) && $this->callbacks) {
			foreach ($this->callbacks as $callback) {
				call_user_func($callback['handler'], $this, $callback['args']);
			}
		}
		return 0 == count($this->errors);
	}

	public function render()
	{
		return '<input' . Html::attributes(array_merge($this->attributes, array(
			'type'  => 'file',
			'id'    => $this->composeId(),
			'name'  => $this->name . '[]'
		))) . ' />';
	}
}


class Form
{
	/**
	 * @var string  Form template.
	 */
	public $template = 'form';

	/**
	 * @var array  Attributes.
	 */
	public $attributes = array();

	/**
	 * @var string  Legend.
	 */
	public $legend;

	/**
	 * @var array  Elements.
	 */
	public $elements = array();

	/**
	 * @var mixed  Width of labels.
	 */
	public $labelWidth;

	/**
	 * @var string  Current fieldset.
	 */
	public $fieldset = false;

	/**
	 * @var array  All fieldsets.
	 */
	public $fieldsets = array();

	/**
	 * Constructor.
	 *
	 * @param string $id      Form Id.
	 * @param string $legend  Legend.
	 * @param string $action  Submit URL.
	 * @return this
	 */
	public function __construct($id, $legend = '', $action = '')
	{
		$this->attributes['action'] = $action;
		$this->attributes['class']  = 'autoform';
		$this->attributes['id']     = $id;
		$this->attributes['method'] = 'post';
		$this->legend = $legend;
	}


	/**
	 * Sets current fieldset.
	 *
	 * @param string $name        Name.
	 * @param string $legend      Legend.
	 * @param string $attributes  Fieldset HTML attributes.
	 * @return this
	 */
	public function fieldset($name = false, $legend = null, $attributes = array())
	{
		$this->fieldset = $name;
		if ($name) {
			if (!isset($this->fieldsets[$name])) {
				$this->fieldsets[$name] = array(
					'legend'     => $legend,
					'attributes' => $attributes,
					'elements'   => array(),
				);
			}
		}
		return $this;
	}

	/**
	 * Validates the form.
	 *
	 * @return boolean
	 */
	public function validate()
	{
		$valid = true;
		if (isset($_POST[$this->attributes['id']])) {
			$post = $_POST[$this->attributes['id']];
			foreach ($this->elements as $el) {
				if ($el instanceof Form_Field_Submit || $el instanceof Form_Field_Html) {
					continue;
				}
				$name = substr($el->name, -2) == '[]' ? substr($el->name, 0, -2) : $el->name;
				if ($el instanceof Form_Field_Checkbox) {
					$value = array_key_exists($name, $post);
				} elseif (array_key_exists($name, $post)) {
					$value = $post[$name];
				} else {
					$value = null;
				}
				$el->modified = $el->value != $value;
				$el->value = $value;
				$valid &= $el->validate();
			}
		} else {
			$valid = false;
		}
		return $valid;
	}

	/**
	 * Loads elements' values.
	 *
	 * @param array $values  Values.
	 * @return this
	 */
	public function loadValues($values)
	{
	  foreach ($values as $key => $value) {
		  if (array_key_exists($key, $this->elements)) {
				$this->elements[$key]->value = $value;
			}
		}
		return $this;
	}

	/**
	 * Returns elements values.
	 *
	 * @return array
	 */
	public function getValues()
	{
		$data = array();
		foreach ($this->elements as $el) {
			if (
			    $el instanceof Form_Field_Submit ||
			    $el instanceof Form_Field_File   ||
			    $el instanceof Form_Field_Html   ||
				$el->phantom
			) {
				continue;
			}
            if ($el->name == 'multiselect1' AND isset($el->value)) {
                foreach ($el->value as $e) {
                    isset($result)?$result.=$e . ',':$result=$e . ',';
//                    $result .=$e . ',';
                }$result=mb_substr($result,0,-1,'utf-8');
                $data[$el->name] = $result;unset($result);
            }
            elseif ($el->name == 'multiselect2' AND isset($el->value)) {
                foreach ($el->value as $e) {
                    isset($result)?$result.=$e . ',':$result=$e . ',';
                }$result=mb_substr($result,0,-1,'utf-8');
                $data[$el->name] = $result;unset($result);
            }
            elseif ($el->name == 'multiselect3' AND isset($el->value)) {
                foreach ($el->value as $e) {
                    isset($result)?$result.=$e . ',':$result=$e . ',';
                }$result=mb_substr($result,0,-1,'utf-8');
                $data[$el->name] = $result;unset($result);
            }else{
                $data[$el->name] = $el->getValue();
            }

		}
		return $data;
	}

	/**
	 * Returns only modified elements values.
	 *
	 * @return array
	 */


//    public function getModified()
//    {
//        $data = array();
//        foreach ($this->elements as $el) {
//            if ($el instanceof Form_Field_Submit ||
//                $el instanceof Form_Field_File   ||
//                $el instanceof Form_Field_Html   ||
//                $el->modified == false
//            ) {
//                continue;
//            }
//            if ($el->name == 'multiselect1' AND isset($el->options)) {
//                foreach ($el->options as $key=>$value) {
//                    isset($result)?$result.=$key . ',':$result=$key . ',';
//                }$result=mb_substr($result,0,-1,'utf-8');
//                $data[$el->name] = $result;
//            } else {
//                $data[$el->name] = $el->getValue();
//            }
//        }     system::dump($data);exit();
//        return $data;
//    }
	public function getModified()
	{
		$data = array();
		foreach ($this->elements as $el) {
			if ($el instanceof Form_Field_Submit ||
				$el instanceof Form_Field_File   ||
				$el instanceof Form_Field_Html   ||
				$el->modified == false
			) {
				continue;
			}
            if ($el->name == 'multiselect1' AND isset($el->value)) {
                foreach ($el->value as $e) {
                    isset($result)?$result.=$e . ',':$result=$e . ',';
//                    $result .=$e . ',';
                }$result=mb_substr($result,0,-1,'utf-8');
                $data[$el->name] = $result;unset($result);
            }
            elseif ($el->name == 'multiselect2' AND isset($el->value)) {
                foreach ($el->value as $e) {
                    isset($result)?$result.=$e . ',':$result=$e . ',';
                }$result=mb_substr($result,0,-1,'utf-8');
                $data[$el->name] = $result;unset($result);
            }
            elseif ($el->name == 'multiselect3' AND isset($el->value)) {
                foreach ($el->value as $e) {
                    isset($result)?$result.=$e . ',':$result=$e . ',';
                }$result=mb_substr($result,0,-1,'utf-8');
                $data[$el->name] = $result;unset($result);
            }else{
                $data[$el->name] = $el->getValue();
            }

            //$data[$el->name] = $result;
		}     // system::dump($this->elements);exit();
		return $data;
	}

	public function clearValues()
	{
		foreach ($this->elements as $el) {
			if ($el instanceof Form_Field_Submit ||
				$el instanceof Form_Field_File   ||
				$el instanceof Form_Field_Html
			) {
				continue;
			}
			$el->value = null;
		}
	}


    /**
     * PHP magic __toString().
     *
     * @return string
     */
    public function __toString()
    {
    	return $this->render();
    }

	/**
	 * Renders begin and end of the form.
	 *
	 * @param string  begin|end - Bound name.
	 * @return string
	 */
    public function renderBound($bound)
    {
    	if ($bound == 'begin' || $bound == 'end') {
        	return (string) new View($this->template, array_merge(get_object_vars($this), array('action' => $bound)));
    	} else {
			throw new InvalidArgumentException('Unsupported bound name "' . $bound . '".');
    	}
    }

    public function render($fieldset = null)
    {
    	if ($fieldset) {
    		if (isset($this->fieldsets[$fieldset])) {
	        	return (string) new View($this->template, array_merge(get_object_vars($this), array(
	        		'fieldset' => $fieldset,
	        		'action' => 'fieldset',
	        		'fieldsets' => $this->fieldsets
	        	)));
    		} else {
				throw new InvalidArgumentException('Fieldset "' . $fieldset . '" does not exists.');
    		}
    	} else {
    		$result = $this->renderBound('begin');
    		$result .= (string) new View($this->template, get_object_vars($this));
    		$result .= $this->renderBound('end');
	        return $result;
    	}
    }

	public function hidden($name, $value = null)
	{
		return $this->elements[$name] = new Form_Field_Hidden($this, $name, $value);
	}

	public function text($name, $label = null, $value = null)
	{
		return $this->elements[$name] = new Form_Field_Text($this, $name, $label, $value);
	}

	public function password($name, $label = null, $value = null)
	{
		return $this->elements[$name] = new Form_Field_Password($this, $name, $label, $value);
	}

	public function select($name, $options, $label = null, $value = null)
	{
		return $this->elements[$name] = new Form_Field_Select($this, $name, $options, $label, $value);
	}
    public function multiselect($name, $options, $label = null, $value = null)
    {
        return $this->elements[$name] = new Form_Field_Multiselect($this, $name, $options, $label, $value);
    }
	public function radio($name, $options, $label = null, $value = null)
	{
		return $this->elements[$name] = new Form_Field_Radio($this, $name, $options, $label, $value);
	}

	public function checkbox($name, $label = null, $boxLabel = null, $value = null)
	{
		return $this->elements[$name] = new Form_Field_Checkbox($this, $name, $label, $boxLabel, $value);
	}

	public function textarea($name, $label = null, $value = null)
	{
		return $this->elements[$name] = new Form_Field_Textarea($this, $name, $label, $value);
	}

	public function html($name, $label = null, $value = null)
	{
		return $this->elements[$name] = new Form_Field_Html($this, $name, $label, $value);
	}

	public function comment($name)
	{
		return $this->elements[$name] = new Form_Field_Comment($this, $name);
	}

	public function file($name, $label = null)
	{
		return $this->elements[$name] = new Form_Field_File($this, $name, $label);
	}

	public function submit($name, $value, $altLink = null)
	{
		return $this->elements[$name] = new Form_Field_Submit($this, $name, $value, $altLink);
	}

}
